<template>
	<div class="play-rect" ref="playRect">
		<!-- v-show="item.isShow" 不能使用v-if指令,会导致缩放后画面消失(v-if条件不成立时是将dom删除) -->
		<div :class="[rectSpilt, item.isSelected ? 'rect-selected' : '', item.amplify ? 'rect-amplify' : '']"
			v-show="item.isShow"
			v-bind:key="item.deviceNo" 
			v-for="(item, index) in rects" 
			@click="getDeviceInfo(item)"
			@dblclick="adjust(index, item)"
			@wheel="handleWheel($event, item)"
			>
			<i v-if="item.deviceNo != null"
			   @click="closeRect(item)" 
			   class="el-icon-close close"
			   style="float:right;">
			</i>
			<video v-if="item.playerType === 'Flvjs' || item.playerType === 'Native'" 
				class="video-element" 
				:ref="'videoElement' + index">
			</video>
			<canvas v-if="item.playerType === 'NodePlayer'" :id="'playerNo' + item.id" class="video-element"></canvas>
			
			<div v-if="item.waitingShow" class="waiting">
		        <p>{{item.waitingText}}</p>
		    </div>
			<!-- 云台控制组件 -->
		    <div v-if="isShowPTZ && item.isSelected && item.isPlaying" 
				class="ptz-control" 
				@click.stop 
				@dblclick.stop>
		        <div class="ptz-circle">
					<!-- 
		            <div class="ptz-btn ptz-up" 
		                 @click.stop="ptzControl($event, item, 'up')"  mousedown已经包含了click事件,所以这里不需要再加@click.stop了
		                 @dblclick.stop
		                 @mousedown.stop="ptzStart($event, item, 'up')"
		                 @mouseup.stop="ptzStop($event, item, 'up')"
		                 @mouseleave.stop="ptzStop($event, item, 'up')">
		            </div>
					 -->
		            <div class="ptz-btn ptz-up" 
		                 @dblclick.stop
		                 @mousedown.stop="ptzStart($event, item, 'up')"
		                 @mouseup.stop="ptzStop($event, item, 'up')"
		                 @mouseleave.stop="ptzStop($event, item, 'up')">
		            </div>
		            <div class="ptz-btn ptz-left" 
		                 @dblclick.stop
		                 @mousedown.stop="ptzStart($event, item, 'left')"
		                 @mouseup.stop="ptzStop($event, item, 'left')"
		                 @mouseleave.stop="ptzStop($event, item, 'left')">
		            </div>
		            <div class="ptz-btn ptz-right" 
		                 @dblclick.stop
		                 @mousedown.stop="ptzStart($event, item, 'right')"
		                 @mouseup.stop="ptzStop($event, item, 'right')"
		                 @mouseleave.stop="ptzStop($event, item, 'right')">
		            </div>
		            <div class="ptz-btn ptz-down" 
		                 @dblclick.stop
		                 @mousedown.stop="ptzStart($event, item, 'down')"
		                 @mouseup.stop="ptzStop($event, item, 'down')"
		                 @mouseleave.stop="ptzStop($event, item, 'down')">
		            </div>
		        </div>
		    </div>
		</div>
	</div>
</template>
<style scoped>


.close {
	font-size: 22px;
	color: red;

	&:hover {
		cursor: pointer;
	}
	position: relative;  
	z-index: 3; /* 确保在 video 元素之上 */  
}

.play-rect {
	margin-top: 5px;
	/*border: 1px solid rgb(64, 158, 255);*/
	display: flex;
	flex-wrap: wrap;
	/* 使得边框和填充包含在宽度和高度内 */
	box-sizing: border-box;

	.rect {
		border: 1px solid rgb(64, 158, 255);
		box-sizing: border-box; /* 使得边框和填充包含在宽度和高度内 */
		position: relative; /* 可以设置为 relative，以便通过 z-index 控制堆叠顺序 */
		/*transition: width 0.3s, height 0.3s;*/ /* 添加放大过渡效果 */
		
		/*播放窗口中的关闭按钮默认隐藏,悬浮时展现*/
		.close{
			display:none;
		}
		&:hover {
			.close{
				display:block;
			}
			.video-element{
				margin-top: -22px;
			}
		}

	}

	.rect-selected {
		border: 1px solid rgb(64, 158, 255); /* 保持原边框 */
		outline: 2px solid rgb(64, 158, 255); /* 添加outline */
		outline-offset: -1px; /* 让outline向内扩展 */
	}

	.rect-amplify {
		/* 占满整行 */
		/*flex: 1 1 100% !important;*/
		/* 占满整个父div的高度 */
		/*height: 100% !important;*/

		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/1 - 0px) !important;
		/* 边框样式，宽度为0px*/
		height: calc(100%/1 - 0px) !important;


	}
	.rect-split-1 {
		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/1 - 0px);
		/* 边框样式，宽度为0px*/
		height: calc(100%/1 - 0px);
		
	}

	.rect-split-2 {
		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/2 - 0px);
		/* 边框样式，宽度为0px*/
		height: calc(100%/2 - 0px);
	}

	.rect-split-3 {
		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/3 - 0px);
		/* 边框样式，宽度为0px*/
		height: calc(100%/3 - 0px);
	}

	.rect-split-4 {
		/* 减去边框的宽度*/
		flex: 0 0 calc(100%/4 - 0px);
		/* 边框样式，宽度为0px*/
		height: calc(100%/4 - 0px);
	}
	.video-element{
		width:100%;
		object-fit: fill; /*填满整个div,两侧不留空白*/
		height:100%;
		position: relative; /* 可以设置为 relative，以便通过 z-index 控制堆叠顺序 */  
    	z-index: 1; /* 确保 video 在其他内容之下，但在背景之上 */ 
	}
	.waiting{
		width:100%;
		height:100%;
		color:aqua;
		background-color: black;	
		position: absolute;
		top:0;
		p{
			text-align: center;
		}
	}

	.ptz-control {
		position: absolute;
		bottom: 10px;
		right: 10px;
		z-index: 10; /* 确保在最顶层 */
	}

	.ptz-circle {
		width: 120px;
		height: 120px;
		border-radius: 50%;
		background-color: #46a6ff;
		position: relative;
		display: flex;
		justify-content: center;
		align-items: center;
		box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); /* 添加阴影效果 */
	}

	.ptz-btn {
		position: absolute;
		width: 0;
		height: 0;
		border-style: solid;
		cursor: pointer;
		transition: all 0.2s ease;
		/* 使用transform-origin确保缩放时保持中心位置 */
    	transform-origin: center;
	}

	.ptz-btn:hover {
		/* 使用scale而不是translate来避免偏移，并设置transform-origin */
    	transform: scale(1.2);
	}

	.ptz-up {
		top: 10px;
		left: 50%;
		transform: translateX(-50%);
		border-width: 0 16px 24px 16px;
		border-color: transparent transparent #ffffff transparent;
	}

	.ptz-up:hover {
		transform: translateX(-50%) scale(1.2); 
	}

	.ptz-down {
		bottom: 10px;
		left: 50%;
		transform: translateX(-50%);
		border-width: 24px 16px 0 16px;
		border-color: #ffffff transparent transparent transparent;
	}

	.ptz-down:hover {
		transform: translateX(-50%) scale(1.2); 
	}

	.ptz-left {
		left: 10px;
		top: 50%;
		transform: translateY(-50%);
		border-width: 16px 24px 16px 0;
		border-color: transparent #ffffff transparent transparent;
	}

	.ptz-left:hover {
		transform: translateY(-50%) scale(1.2); 
	}

	.ptz-right {
		right: 10px;
		top: 50%;
		transform: translateY(-50%);
		border-width: 16px 0 16px 24px;
		border-color: transparent transparent transparent #ffffff;
	}

	.ptz-right:hover {
		transform: translateY(-50%) scale(1.2);
	}
	
}
</style>

<script>
import { mapState } from 'vuex';
import flvjs from 'flv.js'; 
import { NodePlayer } from "./NodePlayer-full.min.js";
import {playVideo,closeVideo,control} from '@/api/vplat/device'; 

export default {
	name: "PlayWindow",
	dicts: [],
	//props: ['rectSize','isShowPTZ'],
	props: {
		rectSize: {
			type: Number,
			required: true
		},
		isShowPTZ: {
			type: Boolean,
			default: true
		}
	},
	data() {
		return {
			rects: [],//存储播放方格数据信息
			//rects: Array.from({ length: 9 }, (_, i) => ({ id: i, isSelected: false })),
			rectSpilt: 'rect rect-split-2',//初始化播放窗口的分屏样式
			intervalTime: 30 * 1000,
			ptzInterval: null, // 用于存储云台连续控制的定时器
			currentPtzItem: null, // 当前正在控制的设备
			currentPtzDirection: null, // 当前正在控制的方向
			wheelZoomTimeout: null, // 用于管理滚轮缩放的自动停止
			isZoomOperation: false, // 标记当前是否为 zoom 操作
			

		};
	},
	mounted() {
		// 使用 $nextTick 确保在 DOM 更新完成后设置高度
		this.$nextTick(() => {
			this.setFullHeight();
			this.setRects(this.rectSize);
			// 添加初始分屏样式设置
			let squareRoot = Math.sqrt(this.rectSize);
			this.rectSpilt = 'rect rect-split-' + squareRoot;
		});

		// 监听窗口大小变化
		//window.addEventListener('resize', this.setFullHeight);

	},
	computed: {//每次渲染都会触发
		...mapState({
			//引入模块化变量
			toPlayDevice: state => state.video.playDevice,//新增播放的设备

		}),
	},
	watch: {
		//监听模块化变量变化 
		toPlayDevice(device) {//监听新增播放的设备
			if (device) {
				const exist = this.rects.find(rect => rect.deviceNo === device.id);
				if (!exist) {
					for (let i = 0; i < this.rects.length; i++) {
						let item = this.rects[i];
						if (item.deviceNo == null) {
							item.deviceNo = device.id;
							item.name = device.name;
							if(device.url){
								item.url = device.url;
							}
							this.active(item);
							this.getPlayUrls(item);
							break;
						}
					}
				}
			}
		},
	},
	beforeDestroy() {
		this.closeAll();
		//window.removeEventListener('resize', this.setFullHeight); // 防止内存泄漏
	},
	methods: {
		// 处理鼠标滚轮缩放
		handleWheel(event, item) {
			// 仅在播放中才响应
			if (!item.isPlaying) return;

			// 阻止页面滚动
			event.preventDefault();
			event.stopPropagation();

			// 获取滚动方向：deltaY < 0 表示向上滚动（放大），> 0 表示向下滚动（缩小）
			// let delta = event.deltaY;
			// let isZoomIn = delta < 0; // 向上滚 = 放大
			const direction = event.deltaY < 0 ? 'in' : 'out';

			// 1. 立即发送 zoom 命令
			this.ptzControl(event, item, direction);

			// 2. 清除之前的缩放停止定时器（防抖）
			if (this.wheelZoomTimeout) {
				clearTimeout(this.wheelZoomTimeout);
			}

			// 3. 设置新的停止定时器
			this.wheelZoomTimeout = setTimeout(() => {
				// 注意：此时可能 item 已被关闭或切换，需校验
				if (this.currentPtzItem?.deviceNo === item.deviceNo && 
					this.currentPtzDirection === direction) {
					// 发送 stop 命令
					const stopEvent = new Event('wheel-stop'); // 伪造事件，避免 null
					this.ptzStop(stopEvent, item, direction);
				}
				this.wheelZoomTimeout = null;
			}, 1000);
		},
		// 云台控制方法
		ptzControl(event, item, direction) {
			// 阻止事件冒泡到父级元素
        	event.stopPropagation();
			console.log(`PTZ Control - Device: ${item.deviceNo}, Direction: ${direction}`);
			// 记录当前控制的设备和方向
			this.currentPtzItem = item;
			this.currentPtzDirection = direction;
			// 标记是否为 zoom 操作
			this.isZoomOperation = ['in', 'out'].includes(direction);
			
			// 云台控制逻辑
			let params = {
				deviceNo: item.deviceNo,
				clientId: "web",
				zoom: 0,
				tilt: 0,
				pan: 0,
				focus: 0,
				iris: 0,
				speed: 100,
				stop: 0

			}
			if (direction == 'up') {
				params.tilt = 1
			} else if (direction == 'down') {
				params.tilt = 2
			} else if (direction == 'left') {
				params.pan = 1
			} else if (direction == 'right') {
				params.pan = 2
			} else if (direction == 'in') {
				params.zoom = 2
				params.speed = 0
			} else if (direction == 'out') {
				params.zoom = 1
				params.speed = 0
			}
			control(params).then((res) => {
				console.log('云台控制成功', res);
			}).catch((err) => {
				console.error('云台控制失败', err);
			});

		},
		// 云台双击控制方法（如果需要特殊处理）
		// ptzControlDblClick(event, item, direction) {
		// 	// 阻止事件冒泡到父级元素
		// 	event.stopPropagation();
		// 	console.log(`PTZ Double Click Control - Device: ${item.deviceNo}, Direction: ${direction}`);
		// },
		// 云台开始控制方法（鼠标按下事件）
		ptzStart(event, item, direction) {
			// 阻止事件冒泡到父级元素
			event.stopPropagation();
			
			console.log(`PTZ Start - Device: ${item.deviceNo}, Direction: ${direction}`);
			
			// 如果已经有正在执行的云台控制，先停止
			if (this.ptzInterval) {
				this.ptzStop();
			}
			
			// 记录当前控制的设备和方向
			this.currentPtzItem = item;
			this.currentPtzDirection = direction;
			
			// 立即发送一次控制命令
			//this.sendPtzCommand(item, direction, 'start');
			this.ptzControl(event, item, direction);
			
			// 设置定时器，持续发送控制命令
			/*
			this.ptzInterval = setInterval(() => {
				this.ptzControl(event, item, direction);
			}, 200); // 每200ms发送一次控制命令
			*/
			
			// 发出开始控制的事件
			// this.$emit('ptz-start', { deviceNo: item.deviceNo, direction: direction });
		},
		
		// 云台停止控制方法（鼠标松开或离开事件）
		ptzStop(event, item, direction) {
			// 阻止事件冒泡（如果 event 存在）
			if (event?.stopPropagation) {
				// 阻止事件冒泡到父级元素
				event.stopPropagation();
			}
			// if (item == null) {
			// 	item = this.currentPtzItem;
			// }		
			// 清除定时器
			if (this.ptzInterval) {
				clearInterval(this.ptzInterval);
				this.ptzInterval = null;
			}
			// 确定当前要操作的设备和方向
			const finalItem = item || this.currentPtzItem;
			const finalDirection = direction || this.currentPtzDirection;
			const isZoomOp = this.isZoomOperation;
			
			// 发送 STOP 命令（停止所有动作）
			if (finalItem) {
				const stopParams = {
					deviceNo: finalItem.deviceNo,
					clientId: "web",
					zoom: 0,
					tilt: 0,
					pan: 0,
					focus: 0,
					iris: 0,
					speed: 0,
					stop: 1
				};
				control(stopParams)
				.then(res => console.log('云台停止成功', res))
				.catch(err => console.error('云台停止失败', err));
			}
			
			// 如果是 zoom 操作，停止后自动聚焦(根据需要是否启动此功能)
			/*
			if (isZoomOp && finalItem && finalDirection) {
				let focusAction = 0;
				if (finalDirection === 'in') {
					focusAction = 1; // 近焦
				} else if (finalDirection === 'out') {
					focusAction = 2; // 远焦
				}

				if (focusAction !== 0) {
					// 使用闭包安全捕获 finalItem 和 focusAction
					setTimeout(() => {
						const focusParams = {
							deviceNo: finalItem.deviceNo,
							clientId: "web",
							zoom: 0,
							tilt: 0,
							pan: 0,
							focus: focusAction,
							iris: 0,
							speed: 0,
							stop: 0
						};
						control(focusParams)
							.then(res => console.log('自动聚焦成功', res))
							.catch(err => console.error('自动聚焦失败', err));
					}, 500);
				}
			}
			*/

			// 清空当前控制状态
			this.currentPtzItem = null;
			this.currentPtzDirection = null;
			this.isZoomOperation = false; // 重置标志
			
			// 发出停止控制的事件
			// if (item && direction) {
			// 	this.$emit('ptz-stop', { deviceNo: item.deviceNo, direction: direction });
			// }
		},
		//关闭所有播放窗口
		closeAll() {
			this.rects.forEach((o, i) => {
				this.closeRect(o);
			});
		},
		//创建播放器,加载视频
		createPlayer(item) {
			/** 播放器类型,区分h265和h265播放器 [Flvjs | NodePlayer]*/
			let playerType = "Flvjs";			
			let playUrl = item.playUrls?.websocketUrl;

			// 若url包含265,此时 采用265播放器播放;也可以使用其他方式区分H.265码流的设备
			if (playUrl.includes("H265")) {
				playerType = "NodePlayer";
			}if (playUrl?.endsWith('.mp4')) {
				playerType = "Native"; // 原生播放
			} 


			item.playerType = playerType;
			//需要确保页面上的video标签或canvas标签出现后再往下执行
			this.$nextTick(() => {
				if(playerType == "Flvjs"){
					item.videoElement = this.$refs['videoElement' +  item.id][0];
					this.createFlvjsPlayer(item);
				}else if(playerType == "NodePlayer"){
					this.createNodePlayer(item);
				}else if (playerType === "Native") {
					item.videoElement = this.$refs['videoElement' + item.id][0];
					item.videoElement.src = playUrl;
					item.videoElement.play().catch(e => console.warn("自动播放被阻止:", e));
					item.player = item.videoElement; // 便于统一管理
				}
			});
		},
		createNodePlayer(item) {
			NodePlayer.load(() => {
			      let player = new NodePlayer();
			      /**
			       * 自动测试浏览器是否支持MSE播放，如不支持，仍然使用软解码。
			       * 紧随 new 后调用
			       * 不调用则只使用软解
			       */
			      // player.useMSE();

			      /**
			       * 开启屏幕常亮
			       * 在手机浏览器上,canvas标签渲染视频并不会像video标签那样保持屏幕常亮
			       * 如果需要该功能, 可以调用此方法
			       * H5目前在chrome\edge 84, android chrome 84及以上有原生亮屏API, 需要是https页面
			       * 其余平台为模拟实现，非全兼容。
			       */
			      player.setKeepScreenOn();

			      /**
			       * 传入 canvas视图的id，当使用mse时，自动转换为video标签
			       */
			      let view = "playerNo" + item.id;
			      player.setView(view);

			      /**
			       * 设置最大缓冲时长，单位毫秒，只在软解时有效
			       */
			      player.setBufferTime(1000);

			      /**
			       * 设置超时时长, 单位秒,只在软解时有效
			       * 在连接成功之前和播放中途,如果超过设定时长无数据返回,则回调timeout事件
			       */
			      // player.setTimeout(10);
			      
			      /*
			      player.on("start", () => {
			        console.log("player on start");
			      });
			      */
				 
			      /*
			      player.on("stop", () => {
			        console.log("player on stop");
			      });
			      */

			      /*
			      player.on("error", (e) => {
			        console.log("player on error", e);
			      });
			      */
				  
			      /*
			      player.on("videoInfo", (w, h, codec) => {
			        console.log("player on video info width=" + w + " height=" + h + " codec=" + codec);
			      });
			      
			      player.on("audioInfo", (r, c, codec) => {
			        console.log("player on audio info samplerate=" + r + " channels=" + c + " codec=" + codec);
			      });
			      */
			      
			      /*
			       player.on("metadata", (metadata) => {
			         var m = NodePlayer.AMF.parseScriptData(metadata.buffer, 0, metadata.length);
			         console.log(m);
			       });
			      */

			      // player.on("videoSei", (sei, pts) => {
			      //   console.log("player on video sei=" + sei + " pts=" + pts);
			      // });
			      
				  /*
			      player.on("videoFrame", (pts) => {
			         console.log("player on videoFrame pts=" + pts);
			      });
				  */

			      // player.on("timeout", () => {
			      //   console.log("player on timeout");
			      //   player.stop();
			      // });

			      // player.on("buffer", (state) => {
			      //   console.log("player on buffer state=" + state);
			      // });
				  
			      player.on("stats", (stats) => {
			        //console.log("player on stats=", stats);
			        //自定义播放情况,以判断播放是否异常
			        if(player.nowVbps == null){
			        	player.lastVbps = 0;
			        	player.nowVbps = stats.vbps;
			        }else{
			        	player.lastVbps = player.nowVbps;
			        	player.nowVbps = stats.vbps;
			        }
			        
			      });
				  
			      
			      
			      /**
			       * 设置解密密码,必须是16字节
			       */
			      player.setCryptoKey("");
			      /**
			       * 开始播放,参数为 http-flv或 websocket-flv 的url
			       */
			      player.start(item.playUrls.websocketUrl);
		          item.player = player;
				  // NodePlayer 没有直接的播放成功回调，但我们可以通过 stats 事件判断
        		  // 先设置为 true，如果后续有问题再调整
				  item.isPlaying = true;
        		  item.waitingShow = false;
		          
		          //以下要使用item.player作判断,不能直接用player作判断
		          if(item.intervalId == null){
					  //注册定时器观察播放是否正常
			          let startTime = new Date();	//开始时间
					  let intervalId = setInterval(() => {
						  //自定义播放情况,以判断播放是否异常
						  if(item.player.lastVbps == item.player.nowVbps){
							  this.replayVideo(item);
						  }else{
							  item.player.lastVbps = item.player.nowVbps;
						  }
					  }, this.intervalTime);
					  item.intervalId = intervalId;
				  }
				  
				 
			});

		},
		createFlvjsPlayer(item) {
			if (flvjs.isSupported()) {
				let player = flvjs.createPlayer({
			        type: 'flv',					//媒体类型
			        url: item.playUrls.websocketUrl,//flv格式媒体URL
			        isLive: true,					//数据源是否为直播流
			        //hasAudio: false,				//数据源是否包含有音频
				    hasVideo: true,					//数据源是否包含有视频
				    withCredentials: false, // 是否需要证书认证
		            cors: true, // 开启跨域访问
			    },{
		              enableWorker: false, // 开启flv.js的Worker，多进程运行flv.js提升解析速度可以优化延迟。 default false
		              enableStashBuffer: false, // Enable IO stash buffer. Set to false if you need realtime (minimal latency) for live stream playback, but may stalled if there's network jittering. default true
		              stashInitialSize: 384, // Indicates IO stash buffer initial size. Default is 384KB. Indicate a suitable size can improve video load/seek time.
		              seekType: "range", // 'range' use range request to seek, or 'param' add params into url to indicate request range.
		              lazyLoad: false, // Abort the http connection if there's enough data for playback. default true
		              lazyLoadMaxDuration: 10, // Indicates how many seconds of data to be kept for 'lazyLoad'.
		              lazyLoadRecoverDuration: 10, // Indicates the 'lazyLoad' recover time boundary in seconds.
		              deferLoadAfterSourceOpen: false, // Do load after MediaSource sourceopen event triggered. On Chrome, tabs which be opened in background may not trigger sourceopen event until switched to that tab. default true
		              //statisticsInfoReportInterval: 600,
		              fixAudioTimestampGap: false, //主要是这个配置，直播时音频的码率会被降低，直播游戏时背景音会有回响，但是说话声音没问题 Fill silent audio frames to avoid a/v unsync when detect large audio timestamp gap. default true
		              // 下面这个控制很有用，主要是用来控制video播放器缓冲区this.buffered中的保留数据的；
		              // 当this.buffered.end(this.buffered.length - 1) - this.buffered.start(0) >= autoCleanupMaxBackwardDuration秒时，
		              // 立即对缓冲区中的数据进行清理，确保只保留最后的autoCleanupMinBackwardDuration秒数据
		              // 所以这个设置能够减少浏览器占用的内存空间，对于同时播放n路视频来说简直是太有意义了
		              // 因为在progress事件里面我的追帧策略是最高16倍速时追帧到16秒内，所以这里autoCleanupMinBackwardDuration的值要大于16
		              autoCleanupSourceBuffer: true, // Do auto cleanup for SourceBuffer. default false.
		              autoCleanupMaxBackwardDuration: 10, // When backward buffer duration exceeded this value (in seconds), do auto cleanup for SourceBuffer. default 3 * 60
		              autoCleanupMinBackwardDuration: 10, // Indicates the duration in seconds to reserve for backward buffer when doing auto cleanup. default 2 * 60
		              reuseRedirectedURL: true,
		            }
			    );
			    
				player.attachMediaElement(item.videoElement);//将播放实例注册到节点
				player.load(); 					//加载数据流
				//player.play();					//播放数据流
				player.play().then(() => {
					// 播放成功
					item.isPlaying = true;
					item.waitingShow = false;
				}).catch(e => {
					console.warn("播放失败:", e);
					item.isPlaying = false;
					item.waitingShow = true;
					item.waitingText = '播放失败，请重试...';
				});
				
				item.player = player;//播放器与播放方格的数据对象绑定
				
				
				item.startPlayTime = new Date();	//开始时间
				if(item.intervalId == null){
					//注册定时器观察播放是否正常,能监听流媒体重启情况
				    let intervalId = setInterval(() => {
				    	/*
					    console.log(videoElement.currentTime);//设置或者返回视频中的当前播放位置（以秒为单位）
					    console.log(videoElement.duration);//返回视频的长度（以秒为单位）
					    console.log(videoElement.ended);//返回视频的播放是否以及结束
					    console.log(videoElement.error);//返回视频错误状态的MediaError对象
					    console.log(videoElement.currentSrc);
					    console.log(videoElement.paused);
					    console.log(videoElement.muted);
					    console.log(videoElement.muted);
					    console.log(videoElement.volume);
					    console.log(videoElement.width);
					    console.log(videoElement.height);
					    */
				    
					    let sec = (new Date().getTime() - item.startPlayTime.getTime())/1000;
					    if(sec - item.videoElement.currentTime > 30){
					    	console.log("播放异常");
					    	this.replayVideo(item);
					    }
					}, this.intervalTime);
					item.intervalId = intervalId;//播放器与播放方格的数据对象绑定
					
					/*
			        player.on("error", (err, msg, time) => {//不能监听流媒体重启情况
			        	console.error('播放错误:', err, msg, time);  
			        	_this.replayVideo("资源播放异常",playerNo,deviceNo);
			        });
					player.on("play", () => {
			        	console.log('开始播放');
			        	item.waitingShow = false;
			        });
					player.on("pause", () => {
			        	console.log('暂停播放');  
			        });
					player.on("ended", () => {
			        	console.log('播放结束');  
			        });
			        */
				}
			    
			    
				
				
			}else{
				console.warn('flv.js is not supported in your browser');
			}

		},
		//发送请求获取播放url
		getPlayUrls(item) {
			// 如果 item.url 存在，则直接播放，跳过请求
			if (item.url) {
				// 可选：设置默认 playUrls 结构（如果播放器依赖该结构）
				item.playUrls = {
					websocketUrl: item.url,
					httpFlvUrl: item.url,
					rtmpUrl: item.url,
					wsFlvUrl: item.url
				};
				this.createPlayer(item);
				return; // 直接返回，不执行后续网络请求
			}

			try{
				//打开视频缓冲提示
				item.waitingShow = true;
				item.waitingText = '等待视频流,请稍后...';
				//请求视频播放url
				/*
				let result = {
				    "param": {
				        //"httpFlvUrl": "https://live.nodemedia.cn:8443/live/tcl_265.flv",
				        "httpFlvUrl": "http://192.168.7.219:8000/live/20230925044756.flv",
				        "rtmpUrl": " rtmp://47.107.226.11:1935/live/20230925044756",
				        "websocketUrl": "ws://192.168.7.219:8000/live/20230925044756.flv",
				        "iMessageCode": 0,
				        "wsFlvUrl": " ws://192.168.7.219:8000/live/20230925044756.flv"
				    }
				};
				item.playUrls = result.param;
				this.createPlayer(item);
				*/
				
				playVideo({deviceNo:item.deviceNo}).then(response => {
					if(response.param == null || response.param.iMessageCode != 0) {
						item.waitingShow = true;
						item.isPlaying = false;
						item.waitingText = '播放异常,稍候尝试...' + response.param.szMessageString;
						if(item.intervalId == null){ //item.intervalId == null为首次请求就失败
							this.replayVideo(item);
						}
						
					}else{
						item.playUrls = response.param;
			        	this.createPlayer(item);
					}
			    });
				
			} catch (e) {
				console.log("播放异常,30秒后得试", e);
				item.waitingShow = true;
				item.isPlaying = false;
				item.waitingText = '播放异常,稍候尝试...' + e.toString();
				
				if(item.intervalId == null){ //item.intervalId == null为首次请求就失败
					this.replayVideo(item);
				}
			}
			

		},
		//重新播放
		replayVideo(item) {
			//this.closePlayer(item);
			setTimeout(() => {
				this.closePlayer(item);
				if(item.deviceNo){
					this.getPlayUrls(item);
				}
			}, this.intervalTime);
		},
		//创建新的播放格子
		createRect(index) {
			let item = {
				id: index,//在播放窗口中的索引
				deviceNo: null,//设备编码
				isSelected: false,//是否被选中
				amplify: false,//是否放大
				isShow: true,//是否展示
				videoElement: null,//播放器绑定的dom
				player: null,//播放器
				playUrls: null,//播放的url
				playerType: null,//播放器的类型
				intervalId: null,//监听播放器播放情况的定时任务
				waitingShow : false,//是否等待视频加载
				waitingText : '等待视频流,请稍后...',//视频加载提示
				isPlaying: false, // 是否播放中
				zoom: 0 // 云台缩放级别，初始为0
			};
			return item;

		},
		//双击播放格式缩放
		adjust(index, item) {
			/*
			// 切换当前div的选中状态  
			this.rects[index].amplify = !this.rects[index].amplify;
			// 如果当前div被选中，则取消其他所有div的选中状态  
			if (this.rects[index].amplify) {
				this.rects = this.rects.map((rect, i) => ({
					...rect,
					amplify: i === index,
				}));
			}
			*/

			this.rects.forEach(function (o, i) {
				if (o.id != item.id) {
					o.isShow = !o.isShow;
				}
			});
			item.amplify = !item.amplify;
			item.isShow = true;


		},
		//单击播放格式获取播放信息
		getDeviceInfo(item) {
			this.active(item);
		},
		//设置宫格选中状态
		active(item) {
			//this.rects.map(o => o.isSelected = false);
			this.rects.forEach(function (o, i) {
				o.isSelected = false
			});
			item.isSelected = true;
		},
		//销毁播放器
		closePlayer(item) {
			console.log("closePlayer");
			// 重置播放状态
			item.waitingShow = false;
			item.isPlaying = false;
			let playerType = item.playerType;
			if(playerType == "Flvjs"){
				// 组件销毁前，确保销毁 FlvPlayer 实例  
			    if (item.player != null) {
				  item.videoElement.src = ""; 
			      item.player.pause();  
			      item.player.unload();  
			      item.player.detachMediaElement();  
			      item.player.destroy();  
			      item.player = null;  
			      //console.log("销毁播放器 ",playerType);
			    }
			}else if(playerType == "NodePlayer"){
				//h265播放器
				if (item.player != null) {
					item.player.stop();
			    	item.player.clearView();
			    	item.player = null;
				}
			}
			
		},
		//销毁播放监听的定时器
		closeIntervalId(item){
			console.log("closeIntervalId");
			if(item.intervalId != null){
				clearInterval(item.intervalId);
			}
		},
		//关闭播放格子,销毁播放器
		closeRect(item) {
			let isNativePlay = item.url?.endsWith('.mp4');
			let deviceNo = item.deviceNo;
			this.closePlayer(item);
			this.closeIntervalId(item);
			item.deviceNo = null;
			item.playerType = null;
			if (isNativePlay) {
				return;
			} 
			closeVideo({deviceNo:deviceNo}).then(response => {
		       	
		    });
			
			
		},
		//分页播放
		pagePlay(list) {
			console.log(list);
		},
		//切换分屏数量
		spiltScreen(newSize) {
			//切换前先要恢复非放大的样式,否则切换后样式计算错误
			this.rects.forEach(function (o, i) {
				o.isShow = true;
				o.amplify = false;
			});
			//求一个数的平方根
			let squareRoot = Math.sqrt(newSize);
			this.rectSpilt = 'rect rect-split-' + squareRoot;
			this.setRects(newSize);

		},
		//全屏
		fullScreen() {
			const elem = this.$refs.playRect;
			if (!document.fullscreenElement &&
				!document.mozFullScreenElement && !document.msFullscreenElement &&
				!document.webkitFullscreenElement && !document.webkitCurrentFullScreenElement) {
				// 当前浏览器未处于全屏状态  
				if (elem.requestFullscreen) {
					elem.requestFullscreen().catch(err => {
						console.error("Error attempting to enable full-screen mode:", err.message);
					});
				} else if (elem.mozRequestFullScreen) { /* Firefox */
					elem.mozRequestFullScreen().catch(err => {
						console.error("Error attempting to enable full-screen mode:", err);
					});
				} else if (elem.webkitRequestFullscreen) { /* Chrome, Safari 和 Opera */
					elem.webkitRequestFullscreen().catch(err => {
						console.error("Error attempting to enable full-screen mode:", err);
					});
				} else if (elem.msRequestFullscreen) { /* IE/Edge */
					elem.msRequestFullscreen().catch(err => {
						console.error("Error attempting to enable full-screen mode:", err);
					});
				}
			} else {
				// 当前浏览器已处于全屏状态  
				if (document.exitFullscreen) {
					document.exitFullscreen();
				} else if (document.mozCancelFullScreen) { /* Firefox */
					document.mozCancelFullScreen();
				} else if (document.webkitExitFullscreen) { /* Chrome, Safari 和 Opera */
					document.webkitExitFullscreen();
				} else if (document.msExitFullscreen) { /* IE/Edge */
					document.msExitFullscreen();
				}
			}
		},
		//设备播放格子数量
		setRects(newSize) {
			if (newSize > this.rectSize) {//增加宫格
				for (let i = this.rectSize; i < newSize; i++) {
					this.rects.push(this.createRect(i));

				}
			} else if (newSize < this.rectSize) {//减少宫格
				let tmpArr = [];
				for (let i = 0; i < this.rectSize; i++) {
					let item = this.rects[i];
					if(i < newSize){
						tmpArr.push(item);
					}else{
						this.closeRect(item);
					}
					
				}
				//this.$store.dispatch('video/addPlayAction', null);
				//this.$store.commit('video/addPlay', data);
				this.rects = tmpArr;
			} else {
				if (this.rects.length == 0) {//初始化
					for (let i = 0; i < this.rectSize; i++) {
						this.rects.push(this.createRect(i));
					}
				}
			}
			this.rectSize = newSize;
			//console.log(this.rects);


		},
		//设备播放窗口占满整个div剩余高度
		setFullHeight() {
			let playRect = this.$refs.playRect;
			//方式一:
			var offsetTop = this.getElementOffsetTop(playRect);
			//方式二:
			//var offsetTop = playWindow.getBoundingClientRect().top;
			//浏览器窗口高度
			var browserHeight = window.innerHeight;
			//使有js动态设置高度,20及5为外层div、自身的padding/margin引起的偏移(可通过观察盒子模型得到)
			playRect.style.height = (browserHeight - offsetTop - 20 - 5) + "px";
		},
		//获取html元素距顶部的固定距离
		getElementOffsetTop(element) {
			var top = 0;
			while (element) {
				top += element.offsetTop || 0;
				element = element.offsetParent;
			}
			return top;
		},
	}
};
</script>

